﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;

namespace EcoSystem
{
    public class OrganizationAppService : CrudAppService<
        Organization,
        OrganizationDto,
        Guid,
        GetOrganizationInput,
        CreateOrUpdateOrganizationDto,
        CreateOrUpdateOrganizationDto>, IOrganizationAppService
    {
        public IRepository<SystemUser, Guid> UserRepository { get; set; }

        public OrganizationAppService(IRepository<Organization, Guid> repository) : base(repository)
        {

        }

        public override async Task<OrganizationDto> CreateAsync(CreateOrUpdateOrganizationDto input)
        {
            var query = await Repository.GetQueryableAsync();
            if (input.ParentId != Guid.Empty)
            {
                var pType = query.Where(x => x.Id == input.ParentId).Select(x => x.NodeType).FirstOrDefault();
                if (pType == StructureNodeType.User)
                {
                    throw new UserFriendlyException("参数错误，无效的上级机构");
                }
                if (pType == StructureNodeType.Department && (input.NodeType == StructureNodeType.Enterprise || input.NodeType == StructureNodeType.Region))
                {
                    throw new UserFriendlyException("部门下不允许添加企业或区域组织");
                }
            }
            return await base.CreateAsync(input);
        }

        public override async Task<OrganizationDto> UpdateAsync(Guid id, CreateOrUpdateOrganizationDto input)
        {
            return await base.UpdateAsync(id, input);
        }

        public override async Task DeleteAsync(Guid id)
        {
            if (await Repository.AnyAsync(x => x.ParentId == id))
            {
                throw new UserFriendlyException("当前机构下已存在用户或下级机构信息，无法删除");
            }
            await base.DeleteAsync(id);
        }

        public override async Task<PagedResultDto<OrganizationDto>> GetListAsync(GetOrganizationInput input)
        {
            var query = (await Repository.GetQueryableAsync()).AsNoTracking()
                .Where(x => x.NodeType != StructureNodeType.User)
                .WhereIf(!input.GroupName.IsNullOrWhiteSpace(), x => x.GroupName == input.GroupName)
                .WhereIf(!input.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(input.Name) || x.EnCode.StartsWith(input.Name))
                .WhereIf(!input.Code.IsNullOrWhiteSpace(), x => x.EnCode.StartsWith(input.Code))
                .WhereIf(input.NodeType.HasValue, x => input.NodeType.Value.HasFlag(x.NodeType))
                .WhereIf(input.ParentId.HasValue, x => x.ParentId == input.ParentId)
                .WhereIf(input.Status.HasValue, x => x.Status == input.Status);
            var total = await query.CountAsync();
            var items = await query.OrderByDescending(x => x.Id).PageBy(input).ToListAsync();
            return new PagedResultDto<OrganizationDto>()
            {
                Items = await MapToGetListOutputDtosAsync(items),
                TotalCount = total
            };
        }

        public async Task<List<OrganizationDto>> GetAllListAsync(GetOrganizationInput input)
        {
            var query = (await Repository.GetQueryableAsync()).AsNoTracking()
                .Where(x => x.NodeType != StructureNodeType.User)
                .WhereIf(!input.GroupName.IsNullOrWhiteSpace(), x => x.GroupName == input.GroupName)
                .WhereIf(!input.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(input.Name) || x.EnCode.StartsWith(input.Name))
                .WhereIf(!input.Code.IsNullOrWhiteSpace(), x => x.EnCode.StartsWith(input.Code))
                .WhereIf(input.NodeType.HasValue, x => input.NodeType.Value.HasFlag(x.NodeType))
                .WhereIf(input.ParentId.HasValue, x => x.ParentId == input.ParentId)
                .WhereIf(input.Status.HasValue, x => x.Status == input.Status);
            var items = await query.OrderByDescending(x => x.Id).ToListAsync();
            return await MapToGetListOutputDtosAsync(items);
        }

        /// <summary>
        /// 返回目录树结构
        /// </summary>
        /// <param name="bucketName"></param>
        /// <returns></returns>
        public async Task<List<OrganizationTreeDto>> GetTreeAsync(GetOrganizationInput input)
        {
            var query = (await Repository.GetQueryableAsync()).AsNoTracking()
                .Where(x => x.NodeType != StructureNodeType.User)
                .WhereIf(!input.GroupName.IsNullOrWhiteSpace(), x => x.GroupName == input.GroupName)
                .WhereIf(!input.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(input.Name) || x.EnCode.StartsWith(input.Name))
                .WhereIf(!input.Code.IsNullOrWhiteSpace(), x => x.EnCode.StartsWith(input.Code))
                .WhereIf(input.NodeType.HasValue, x => input.NodeType.Value.HasFlag(x.NodeType))
                .WhereIf(input.ParentId.HasValue, x => x.ParentId == input.ParentId)
                .WhereIf(input.Status.HasValue, x => x.Status == input.Status);
            var rows = await query.OrderBy(x => x.Id).ToListAsync();
            var allItems = ObjectMapper.Map<List<Organization>, List<OrganizationTreeDto>>(rows);
            var items = allItems.Where(x => x.ParentId == null || x.ParentId == Guid.Empty).ToList();
            foreach (var item in items)
            {
                var subItems = GetChildrenItems(item.Id, allItems);
                if (subItems.Count > 0)
                {
                    item.Children = subItems;
                }
            }
            return items;
        }

        /// <summary>
        /// 获取下级节点数据
        /// </summary>
        /// <param name="parentId"></param>
        /// <param name="allItems"></param>
        /// <returns></returns>
        private List<OrganizationTreeDto> GetChildrenItems(Guid parentId, List<OrganizationTreeDto> allItems)
        {
            var list = allItems.Where(x => x.ParentId == parentId && x.NodeType != StructureNodeType.User).ToList();
            foreach (var item in list)
            {
                var subItems = GetChildrenItems(item.Id, allItems);
                if (subItems.Count > 0)
                {
                    item.Children = subItems;
                }
            }
            return list;
        }

        public async Task AddUsersAsync(AddOrganizationUsersInputDto input)
        {
            var queryOrg = await Repository.GetQueryableAsync();
            var org = await queryOrg.AsNoTracking().FirstAsync(x => x.Id == input.OrganizationId);
            if (org.NodeType == StructureNodeType.Group ||
                org.NodeType == StructureNodeType.Region ||
                org.NodeType == StructureNodeType.User)
            {
                throw new UserFriendlyException("当前组织架构节点下面不可直接添加用户信息");
            }

            var userIds = input.UserItems.Select(x => x.UserId).ToList();
            var existsIds = await queryOrg.Where(x => x.ParentId == input.OrganizationId && x.NodeType == StructureNodeType.User)
                .Select(x => x.EnCode).ToListAsync();
            var items = new List<Organization>();
            var queryUser = await UserRepository.GetQueryableAsync();
            var users = await queryUser.AsNoTracking().Where(x => userIds.Contains(x.Id)).ToListAsync();
            foreach (var user in users)
            {
                var userId = user.Id.ToString();
                if (existsIds.Any(x => x == userId))
                {
                    continue;
                }
                var depUser = input.UserItems.First(x => x.UserId == user.Id);
                var item = new Organization()
                {
                    GroupName = org.GroupName,
                    ParentId = input.OrganizationId,
                    NodeType = StructureNodeType.User,
                    EnCode = userId,
                    PathCode = org.PathCode + "_USER",
                    CreationTime = DateTime.Now,
                    Name = user.Name,
                    PostName = depUser.PostName,
                    DepName = org.Name,
                    Status = true,
                };
                EntityHelper.TrySetId(item, () => GuidGenerator.Create());
                items.Add(item);
            }
            if (items.Count > 0)
            {
                await Repository.InsertManyAsync(items);
                queryUser = queryUser.Where(x => userIds.Contains(x.Id));
                await queryUser.ExecuteUpdateAsync(x => x
                    .SetProperty(m => m.OrganizeId, org.Id.ToString())
                    .SetProperty(m => m.OrganizeCode, org.PathCode));

            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<List<SystemUserDto>> GetUsersAsync(Guid id)
        {
            var queryUser = await UserRepository.GetQueryableAsync();
            var queryOrg = await Repository.GetQueryableAsync();
            var query = from a in queryUser
                        join b in queryOrg
                        on a.Id.ToString() equals b.EnCode
                        where b.Id == id
                        select new { user = a, org = b };
            var items = await query.AsNoTracking().ToListAsync();
            var users = items.Select(x =>
            {
                var user = ObjectMapper.Map<SystemUser, SystemUserDto>(x.user);
                user.OrganizationId = x.org.Id;
                user.ParentId = x.org.ParentId;
                user.PostName = x.org.PostName;
                user.OrgName = x.org.DepName;
                return user;
            }).ToList();
            return users;
        }

        public async Task<List<SystemUserDto>> GetUsersForCodeAsync(string pathCode)
        {
            var queryUser = await UserRepository.GetQueryableAsync();
            var queryOrg = await Repository.GetQueryableAsync();
            var query = from a in queryUser
                        join b in queryOrg
                        on a.Id.ToString() equals b.EnCode
                        where b.PathCode.StartsWith(pathCode)
                        select new { user = a, org = b };
            var items = (await query.AsNoTracking().ToListAsync());
            var users = items.Select(x =>
            {
                var user = ObjectMapper.Map<SystemUser, SystemUserDto>(x.user);
                user.OrganizationId = x.org.Id;
                user.ParentId = x.org.ParentId;
                user.PostName = x.org.PostName;
                user.OrgName = x.org.DepName;
                return user;
            }).ToList();
            return users;
        }

        public async Task<List<UserDepartmentDto>> GetUserDepartmentsAsync(Guid userId)
        {
            var query = from a in (await Repository.GetQueryableAsync())
                        join b in (await Repository.GetQueryableAsync())
                        on a.ParentId equals b.Id
                        where a.EnCode == userId.ToString() && a.NodeType == StructureNodeType.User
                        select new { user = a, org = b };

            var items = await query.AsNoTracking()
                .Select(x => new UserDepartmentDto()
                {
                    DepCode = x.org.PathCode,
                    DepId = x.org.Id,
                    DepName = x.org.Name,
                    PostName = x.user.PostName,
                    UserId = userId,
                }).ToListAsync();
            return items;
        }
    }
}
